home *** CD-ROM | disk | FTP | other *** search
- /*
- * FORMAT.C
- *
- * This is a general purpose format routine for BBSINDEX. It takes
- * as parameters a format string and a pointer to a file header and creates
- * a string, based on the format string, but containing information from
- * the file header.
- *
- * The format string is similar to a printf format string. The following
- * special sequences are translated into values associated with the
- * fileheader.
- *
- * %a Number of accesses
- * %b{} File type {Binary,Text}
- * %c Comment
- * %d Disk filename
- * %f Full name of disk file
- * %i{} File status {Online,Offline}
- * %k File size in K
- * %l{} File origin {Local,Remote}
- * %n Filename
- * %o Uploader's name (owner)
- * %p Path name of disk file
- * %r Directory number
- * %s Section number or letter
- * %ux Output x n times, where n is length of last {sub}format cmd
- * %v{} File contents {Valid,Invalid}
- * %w Upload date (when)
- * %x File size
- * %y Disk directory number
- * %{...} Format substring in { }
- *
- * The following escape sequences are also recognised:
- *
- * \n - End of line
- * \e - Escape
- * \E - CSI (0x9b)
- * \t - Tab
- * \nnn - Octal number with the appropriate value
- *
- * Any other characters after a \ are treated as normal. This can be used
- * to escape certain characters that have special meaning, such as \ itself,
- * %, and sometimes {, }, and comma.
- *
- * With the % flags, additional characters may appear between the % and the
- * value. Using %c as an example, %20c means make the comment 20 characters
- * wide, padding with spaces as necessary. The comment is aligned to the
- * left. %-20c is similar, but aligns the comment to the right. This can
- * be useful for padding. Items followed by {} are boolean flags, which
- * can be either true or false. Inside the {} are two items, seperated by
- * a comma. If the flag is true, the first value is used, else the second
- * value. The expressions inside {} may not include %.
- *
- * The exception to this is %m, which can contain a complete format
- * string within the {}. In this case, the substring is formatted as
- * normal, and then the resulting string is formatted according to
- * the parameters between the % and m. For example %10m{(%s,%d)} formats
- * the section and directory number in brackets, while ensuring that
- * the total field width is 10 characters.
- *
- * n.b. A file is said to be valid if its actual filesize is equivalent
- * to the filesize in the file catalogue.
- *
- */
-
- #ifndef LATTICE_50
- #include "system.h"
- #endif
-
- #include "bbsindex.h"
-
- #define LEFT 0 /* Left alignment */
- #define RIGHT 1 /* Right alignment */
- #define MAXPATH 256 /* Maximum length of disk path */
-
- #define MIN(a,b) ((a) > (b) ? (b) : (a))
-
- #define LEFTBR '{' /* Left bracket */
- #define RIGHTBR '}' /* Right bracket */
- #define BACKSLASH '\\' /* Backslash */
-
- static int len; /* Length of format option */
- static int align; /* Text alignment - LEFT or RIGHT */
- static int opos; /* Position in output string */
- static int omaxlen; /* Maximum length of output string */
-
- static char buf[256];
-
- static char *sectnums[] = {
- "0", "1", "2", "3", "4", "5", "6", "7",
- "8", "9", "A", "B", "C", "D", "E", "F"
- };
-
- static char *days[] = {
- "Sunday", "Monday", "Tuesday", "Wednesday",
- "Thursday", "Friday", "Saturday"
- };
-
- /*
- * doescape()
- * ----------
- * This function parses the escape sequence passed as a string,
- * and stores the character it corresponds to in the output character.
- * it returns the number of characters taken up by the escape sequence.
- */
- int doescape(out, seq)
- unsigned char *out;
- unsigned char *seq;
- {
- int num = 0;
- char *p;
-
- if (*seq >= '0' && *seq <= '7') {
- p = seq;
- for (p = seq; *p >= '0' && *p <= '7'; p++)
- num = (num * 8) + (*seq - '0');
- *out = num;
- return (p-seq);
- }
- switch (*seq) {
-
- case 't':
- *out = '\t';
- break;
-
- case 'n':
- *out = '\n';
- break;
-
- case 'e':
- *out = '\033';
- break;
-
- case 'E':
- *out = '\233';
- break;
-
- default:
- *out = *seq;
- break;
- }
- return (1);
- }
-
-
- /*
- * makedate()
- * ----------
- * This function takes the passed day, month and year and returns
- * a pointer to a string containing dd-mmm-yy (e.g. 23-Mar-89).
- */
-
- #define ITOA(c1,c2,x) ((c1) = ((x)/10 + '0'),(c2) = (((x) % 10) + '0'))
-
- char *makedate(day, month, year)
- int day, month, year;
- {
- static char buf[10];
-
- ITOA(buf[0], buf[1], day);
- buf[3] = months[month][0];
- buf[4] = months[month][1];
- buf[5] = months[month][2];
- ITOA(buf[7], buf[8], year);
- buf[2] = '-';
- buf[6] = '-';
- buf[9] = CHAR_NULL;
- return (buf);
- }
-
- /*
- * maketime()
- * ----------
- * Returns a pointer to a string containing the specified time, in
- * the form HH:MM:SS.
- */
- char *maketime(secs, mins, hours)
- int secs, mins, hours;
- {
- static char buf[9];
- ITOA(buf[0], buf[1], hours);
- ITOA(buf[3], buf[4], mins);
- ITOA(buf[6], buf[7], secs);
- buf[2] = ':';
- buf[5] = ':';
- buf[8] = CHAR_NULL;
- return (buf);
- }
-
- /*
- * itoa()
- * ------
- * This function returns a pointer to a string containing the ascii
- * representation of the number. The pointer is valid until the next
- * time itoa() is called. Negative numbers are not handled.
- */
- char *itoa(n)
- int n;
- {
- static char buf[20];
- int i = 18;
-
- buf[19] = CHAR_NULL;
- if (n == 0) {
- buf[18] = '0';
- return (buf + 18);
- } else {
- for ( ; n && (i > 0); i--) {
- buf[i] = (n % 10) + '0';
- n = n / 10;
- }
- }
- return (buf+i+1);
- }
-
-
- /*
- * addstring()
- * -----------
- * This functions adds string 's' to string 'out' starting at position
- * (global) opos. If (global) len == 0, then no special formatting is
- * done. Else, the string is formatted in a field of width 'len'
- * characters, truncating or padding with spaces as appropriate. If
- * (global) align == LEFT, then the string is aligned to the left
- * of the field, else to the right.
- *
- * If opos exceeds omaxlen at any point, then no more copying is done.
- */
-
- void addstring(out, s)
- char *out;
- char *s;
- {
- int oleft = omaxlen - opos; /* Number of chars left in output string */
- int slen = strlen(s);
-
- /* Note: Be VERY careful the following hasn't got changed into tabs! */
- static char pad[] = "\
- \
- \
- ";
- /* End of space definition! There should be ~200 spaces */
-
- if (len == 0) { /* No special alignment needed */
- if (oleft > 0)
- strncpy(out+opos,s,MIN(slen,oleft));
- opos = opos + slen;
- } else { /* Do special alignment */
- if (slen > len)
- strncpy(out+opos, s, len);
- else {
- if (align == LEFT) {
- if (oleft > 0)
- /* Copy string into left of field */
- strncpy(out+opos, s, MIN(slen,oleft));
- oleft = oleft - slen;
- if (oleft > 0 && len > slen)
- /* Copy padding in to rest of field */
- strncpy(out+opos+slen, pad, MIN(len-slen, oleft));
- } else { /* Align == RIGHT */
- if (len > slen)
- /* Copy padding into left of field */
- strncpy(out+opos, pad, MIN(oleft, len-slen));
- if (oleft > 0)
- /* Copy string into right of field */
- strncpy(out+opos+(len-slen), s, MIN(slen,oleft-slen));
- }
- }
- opos = opos + len;
- }
- }
-
- /*
- * addnumber()
- * -----------
- * This macro adds the ascii representation of the number n
- * to the output buffer 'out', starting at position opos (global).
- * See addstring() for more details.
- */
-
- #define addnumber(out,n) addstring((out),itoa(n))
-
- /*
- * format()
- * --------
- * This function converts the format string f and the file header into
- * an output string. See above for valid options in the format string.
- * A pointer to the output string is returned. Maxlen is the maximum
- * length of the output string. Checkfiles is a boolean which is true
- * if the dirnum, online and valid fields in the file header are valid.
- * These are normally NOT valid, unless requested by the user.
- *
- * The external variable dirnames is expected to exist. Dirnames is
- * an array of disk dirctories (NOT BBS file directories).
- */
-
- char *format(out, maxlen, f, fhead, checkfiles)
- char *out;
- int maxlen;
- char *f;
- UDHEAD *fhead;
- int checkfiles;
- {
- char subformat[MAXSUB];
- static int lastlen; /* Length of last {sub}format string */
- int fpos = 0; /* Position in format string */
- int flen = strlen(f);
- int bool, numchars;
- char *p, *s, *rightbr;
-
- omaxlen = maxlen-1; /* Make this global for convenience */
- opos = 0;
-
- for (fpos = 0; fpos < flen && opos < maxlen; fpos++) {
- if (f[fpos] == BACKSLASH) {
- fpos += doescape(out+opos, f+fpos+1);
- opos++;
- } else if (f[fpos] != '%')
- out[opos++] = f[fpos];
- else {
- align = LEFT;
- if (f[++fpos] == '-') {
- align = RIGHT;
- fpos++;
- }
- len = 0;
- while (isdigit(f[fpos]))
- len = (len * 10) + f[fpos++] - '0';
-
- switch (f[fpos]) {
-
- case 'a': /* Number of file accesses */
- addnumber(out,fhead->accesses);
- break;
-
- case 'c': /* File comment */
- addstring(out,fhead->desc);
- break;
-
- case 'd':
- addstring(out,fhead->disk_name);
- break;
-
- case 'f':
- if (checkfiles) {
- *buf = CHAR_NULL;
- if (fhead->online) {
- char ch;
- strcat(buf, dirnames[fhead->dirnum]);
- ch = buf[strlen(buf)-1];
- if (ch != '/' && ch != ':')
- strcat(buf, "/");
- }
- strcat(buf,fhead->disk_name);
- addstring(out,buf);
- } else
- addstring(out,fhead->disk_name); /* Maintain formatting */
- break;
-
- /*
- * Note that all the boolean-related options are grouped
- * together, so that they can have common error handling.
- * A bit nasty perhaps, but it works :-)
- *
- * Note that 'i' and 'v' default to being online and
- * valid respectively, if checkfiles hasn't been
- * selected.
- */
-
- case 'b': /* True if file is binary */
- case 'i': /* True if file is online */
- case 'l': /* True if file uploaded locally */
- case 'v': /* True if file is valid */
-
- switch (f[fpos]) {
- case 'b': bool = fhead->bin;
- break;
- case 'i': bool = checkfiles ? fhead->online : 0;
- break;
- case 'l': bool = fhead->local;
- break;
- case 'v': bool = checkfiles ? fhead->valid : 0;
- break;
- }
- /* Make sure { and } surround arguments */
- rightbr = strchr(f+fpos, RIGHTBR);
- if (f[fpos+1] == LEFTBR && rightbr != NULL) {
- p = f + (fpos+2);
- s = buf;
-
- if (bool) { /* Use first argument */
- /* Copy until comma reached */
- /* Handle any escaped characters (\n, \t etc) */
- while (*p && *p != ',' && *p != RIGHTBR) {
- if (*p == BACKSLASH) {
- p = p + doescape(s++, p+1) + 1;
- } else
- *s++ = *p++;
- }
- } else {
- /* As above, but copy second argument */
- p = strchr(p,',');
- if (p) {
- p++;
- while (*p && *p != RIGHTBR) {
- if (*p == BACKSLASH) {
- p = p + doescape(s++, p+1) + 1;
- } else
- *s++ = *p++;
- }
- }
- }
- *s = CHAR_NULL;
- addstring(out,buf);
- /* Skip over braces */
- fpos = (rightbr - f);
- }
- break;
-
- case 'k': /* File size in K */
- addnumber(out, BTOK(fhead->length));
- break;
-
- case 'n': /* Catalogue file name o file */
- addstring(out, fhead->cat_name);
- break;
-
- case 'o': /* Owner of upload */
- addstring(out, fhead->owner);
- break;
-
- case 'p': /* Path name to disk file */
- if (checkfiles && fhead->online)
- addstring(out, dirnames[fhead->dirnum]);
- break;
-
- case 'r': /* Directory number */
- addnumber(out, fhead->dir);
- break;
-
- case 's': /* Section names */
- addstring(out, sectnums[fhead->section]);
- break;
-
- case 'u': /* Underline with next char */
- fpos++;
- numchars = lastlen + ((align == RIGHT) ? -len : len);
- while (numchars > 0 && opos < omaxlen) {
- out[opos++] = f[fpos];
- numchars--;
- }
- break;
-
- case 'w': /* Date */
- addstring(out,
- makedate( (fhead->date & 31), /* Day */
- ((fhead->date>>5)) % 13, /* Month */
- ((fhead->date>>5)) / 13)); /* Year */
- break;
-
- case 'x':
- addnumber(out, fhead->length);
- break;
-
- case 'y':
- addnumber(out, fhead->dirnum);
- break;
-
- case '{': /* Format substring in { } */
- {
- int numbrackets = 0;
- int savepos, savealign, savelen;
- p = &f[fpos + 1];
- /*
- * Now, find the end of the substring. Nested
- * brackets are skipped over.
- */
- while (*p && !(*p == RIGHTBR && numbrackets == 0)) {
- switch (*p) {
- case LEFTBR: numbrackets++; break;
- case RIGHTBR: numbrackets--; break;
- }
- p++;
- }
- if (*p == RIGHTBR) {
- /*
- * To format the substring, we change the
- * closing } into a NULL so that when
- * we call format recursively, it will
- * think that's the end of the string.
- * After format()ing, we restore the
- * bracket.
- */
- savepos = opos;
- savelen = len;
- savealign = align;
- *p = CHAR_NULL;
- /* Format substring */
- format(subformat, MAXSUB, &f[fpos + 1],
- fhead, checkfiles);
- *p = RIGHTBR;
- opos = savepos;
- len = savelen;
- align = savealign;
- addstring(out, subformat);
- fpos = p - f;
- }
- }
- break;
-
- default:
- len = 0;
- buf[0] = '%';
- buf[1] = f[fpos];
- buf[2] = CHAR_NULL;
- addstring(out, buf);
- }
- }
- }
- out[opos] = CHAR_NULL;
- lastlen = opos;
- return (out);
- }
-
- /*
- * echoformat()
- * ------------
- * This function takes a format string (as passed to the ECHO command)
- * and uses it to produce an output string. The format string can
- * contain the same escape sequences list under format(), and also
- * the following special sequences:
- *
- * %b - Number of bytes occupied by files in last LIST command
- * %k - Number of kilobytes occupied by files in last LIST command
- * %m - Number of megabytes occupied by files in last LIST command
- * %n - Number of files in last LIST command
- *
- * %B, %K, %M and %N are similar to the above except that they
- * represent the running totals for all files listed since the
- * last RESET command.
- *
- * %w - The current date, in dd-mmm-yy format.
- * %d - The current day (Monday, Tuesday etc.)
- * %t - The current time (24 hour clock)
- * %u - As for format()
- * %{..} - As for format()
- */
- char *echoformat(out, maxlen, f)
- char *out;
- int maxlen;
- char *f;
- {
- static int lastlen;
- char subformat[MAXSUB];
- int fpos = 0; /* Position in format string */
- int flen = strlen(f);
- int value;
- int numchars;
- struct tm *today;
- long seconds;
- char *p;
-
- /*
- * First of all, setup the time array
- */
- time(&seconds);
- today = localtime(&seconds);
-
- /*
- * Now format output string, according to format string
- */
- omaxlen = maxlen-1;
- opos = 0;
-
- for (fpos = 0; fpos < flen && opos < maxlen; fpos++) {
- if (f[fpos] == BACKSLASH) {
- fpos += doescape(out+opos, f+fpos+1);
- opos++;
- } else if (f[fpos] != '%')
- out[opos++] = f[fpos];
- else {
- align = LEFT;
- if (f[++fpos] == '-') {
- align = RIGHT;
- fpos++;
- }
- len = 0;
- while (isdigit(f[fpos]))
- len = (len * 10) + f[fpos++] - '0';
-
- value = -1;
- switch (f[fpos]) {
-
- case 'b': value = curbytes; break;
- case 'B': value = totalbytes; break;
- case 'k': value = BTOK(curbytes); break;
- case 'K': value = BTOK(totalbytes); break;
- case 'n': value = curfiles; break;
- case 'N': value = totalfiles; break;
- case 'm': value = BTOK(BTOK(curbytes)); break;
- case 'M': value = BTOK(BTOK(totalbytes)); break;
-
- case 'd': /* Today's day */
- addstring(out, days[today->tm_wday]);
- break;
- case 'w': /* Today's date */
- addstring(out, makedate(
- today->tm_mday, /* Day */
- today->tm_mon+1, /* Month */
- today->tm_year )); /* Year */
- break;
-
- case 't': /* Today's time */
- addstring(out,
- maketime(today->tm_sec, today->tm_min, today->tm_hour));
- break;
-
- case 'u': /* Underline with next char */
- fpos++;
- numchars = lastlen + ((align == RIGHT) ? -len : len);
- while (numchars > 0 && opos < omaxlen) {
- out[opos++] = f[fpos];
- numchars--;
- }
- break;
-
- /*
- * Note - I'm a little unhappy about including this code
- * twice, but a nice simple way of generalising it for
- * both format and echoformat doesn't spring to mind.
- */
- case '{': /* Format substring in { } */
- {
- int numbrackets = 0;
- int savepos, savealign, savelen;
- p = &f[fpos + 1];
- /*
- * Now, find the end of the substring. Nested
- * brackets are skipped over.
- */
- while (*p && !(*p == RIGHTBR && numbrackets == 0)) {
- switch (*p) {
- case LEFTBR: numbrackets++; break;
- case RIGHTBR: numbrackets--; break;
- }
- p++;
- }
- if (*p == RIGHTBR) {
- /*
- * To format the substring, we change the
- * closing } into a NULL so that when
- * we call format recursively, it will
- * think that's the end of the string.
- * After format()ing, we restore the
- * bracket.
- */
- savepos = opos;
- savelen = len;
- savealign = align;
- *p = CHAR_NULL;
- /* Format substring */
- echoformat(subformat, MAXSUB, &f[fpos + 1]);
- *p = RIGHTBR;
- opos = savepos;
- len = savelen;
- align = savealign;
- addstring(out, subformat);
- fpos = p - f;
- }
- }
- break;
-
- default:
- len = 0;
- buf[0] = '%';
- buf[1] = f[fpos];
- buf[2] = CHAR_NULL;
- addstring(out, buf);
- }
- if (value != -1)
- addnumber(out, value);
- }
- }
- out[opos] = CHAR_NULL;
- lastlen = opos;
- return (out);
- }
-